
# This function creates a .ps1 file that will generate a hash of all commands and their associated options.
function CreateSvnCompletion
{
	# First get all commands with aliases by parsing the output of svn help.
	$help =  svn help
	
	$svncmds = $help | where { $_ -match '^\s+(\w+)(?:\s\((.*)\))?' } | `
		foreach { 
			$matches[1]; 
			if ($matches[2])
			{
				$matches[2].Split(", ", [System.StringSplitOptions]::RemoveEmptyEntries) 
			}
		}
	
	# now build a hash of all their options by parsing the output of svn help for that command
	$svncmdoptions = @{}
	foreach( $cmd in $svncmds )
	{
		$svncmdoptions[$cmd] = svn help $cmd | where { $_ -match '^\s+(-+\w[\w\-]*)(\s+\[(.*?)\])?' } |`
			foreach { 
				$matches[1]; 
				
				# More than one option for the same (long option)?
				if ($matches[3])
				{
					$matches[3]
				}
			}
	}
	
	# Now output a string that will recreate the hash
	'$svncmdhash=@{``'
	$svncmds | foreach { 
		$opts = ($svncmdoptions[$_] |  foreach{"`"{0}`"" -f $_})
		"`"$_`"" + "=" + [System.String]::Join(",", $opts) +";``"
	}
	"}"
}

# this is the default TabExpansion function (can be obtained by doing "get-content function:TabCompletion")
# with the SVN completion stuff added.
# The begin and process blocks have been added.
function TabExpansion
{
	param($line, $lastWord)
	
	begin
	{
		# Has the completion file already been generated?
		$completionfile = "${env:TEMP}\svncompletion.ps1"
		if ( ! (Test-Path $completionfile) )
		{
			# Nope, create it
			CreateSvnCompletion > $completionfile
		}
		
		# And dotsource it (this will bring 
		. $completionfile
	}
	
	process
	{
	
		# Expand an SVN command?
		if ( $line -match "^\s*svn\s+(\w+)$" )
		{
			$pat = $matches[1] + "*"
			$svncmdhash.keys | Sort-Object | where { $_ -like $pat } 
			return
		}
		# Or an SVN option to an SVN command?
		elseif ( $line -match "^\s*svn\s(\w+).*(-[-\w]*)" )
		{
			$cmd = $matches[1]
			$pat = $matches[2] + "*"
			$svncmdhash[$cmd] | Sort-Object | where { $_ -like $pat } 
			return
		}
		
		# This is the default function to use for tab expansion. It handles simple
		# member expansion on variables, variable name expansion and parameter completion
		# on commands. It doesn't understand strings so strings containing ; | ( or { may
		# cause expansion to fail.
	
	
		switch -regex ($lastWord)
		{
			# Handle property and method expansion...
			'\$(\w+)\.(\w*)' {
				$method = [Management.Automation.PSMemberTypes] 'Method,CodeMethod,ScriptMethod,ParameterizedProperty'
				$variableName = $matches[1]
				$val = Get-Variable -value $variableName
				$pat = $matches[2] + '*'
				Get-Member -inputobject $val | where {$n = $_.name; $n -like $pat -and $n -notmatch '^[ge]et_'} | foreach {
					if ($_.MemberType -band $method)
					{
						# Return a method...
						'$' + $variableName + '.' + $_.name + '('
					}
					else {
						# Return a property...
						'$' + $variableName + '.' + $_.name
					}
				}
				break;
			}
	
			# Handle variable name expansion...
			'(.*^\$)(\w+)$' {
				$prefix = $matches[1]
				$varName = $matches[2]
				foreach ($v in Get-Childitem ('variable:' + $varName + '*'))
				{
					$prefix + $v.name
				}
				break;
			}
	
			# Do completion on parameters...
			'^-([\w0-9]*)' {
				$pat = $matches[1] + '*'
	
				# extract the command name from the string
				# first split the string into statements and pipeline elements
				# This doesn't handle strings however.
				$cmdlet = [regex]::Split($line, '[|;]')[-1]
	
				#  Extract the trailing unclosed block e.g. ls | foreach { cp
				if ($cmdlet -match '\{([^\{\}]*)$')
				{
					$cmdlet = $matches[1]
				}
	
				# Extract the longest unclosed parenthetical expression...
				if ($cmdlet -match '\(([^()]*)$')
				{
					$cmdlet = $matches[1]
				}
	
				# take the first space separated token of the remaining string
				# as the command to look up. Trim any leading or trailing spaces
				# so you don't get leading empty elements.
				$cmdlet = $cmdlet.Trim().Split()[0]
	
				# now get the info object for it...
				$cmdlet = @(Get-Command -type 'cmdlet,alias' $cmdlet)[0]
	
				# loop resolving aliases...
				while ($cmdlet.CommandType -eq 'alias') {
					$cmdlet = @(Get-Command -type 'cmdlet,alias' $cmdlet.Definition)[0]
				}
	
				# expand the parameter sets and emit the matching elements
				foreach ($n in $cmdlet.ParameterSets | Select-Object -expand parameters)
				{
					$n = $n.name
					if ($n -like $pat) { '-' + $n }
				}
				break;
			}
		}
	}
		
}


